package com.paul.web.security;

import com.paul.common.annotation.IgnoreAuth;
import com.paul.common.utils.AnnotationUtil;
import com.paul.redis.utils.RedisUtil;
import com.paul.web.exception.EntryPointUnauthorizedHandler;
import com.paul.web.exception.RestAccessDeniedHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author ：zmk
 * @date ：Created in 2021/11/12 14:11
 * @description： https://blog.csdn.net/shenchaohao12321/article/details/87714141
 */

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private JWTAuthenticationManager jwtAuthenticationManager;
    @Autowired
    private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
    @Autowired
    private RestAccessDeniedHandler restAccessDeniedHandler;

    //    @Value("${security.ant.permit}")
    private String[] permitMatches; // 需要被忽略的url列表
    private String[] comResources = {
            "/favicon.ico",
            "/v2/api-docs", "/v3/api-docs",
            "/swagger-resources/**", "/swagger-ui/**",
            "/swagger-ui.html", "/doc.html", "/webjars/**",
            "/configuration/**", "/druid/**", "/monitoring/**",
            "/file/**", "/images/**",
            "/actuator/**", "/instances/**"};

    @Value("${project.ignore.class.path}")
    private String ignoreClassPath;
    @Autowired
    private AnnotationUtil annotationUtil;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        try {
            List<String> stringList = new ArrayList<>();
//            Collections.addAll(stringList,permitMatches);
            Map<String, Map<String, Object>> allTagAnnotationUrl = annotationUtil.getAllAddTagAnnotationUrl(
                    ignoreClassPath,
                    IgnoreAuth.class);
            for (String string : allTagAnnotationUrl.keySet()) {
                if (stringList.contains(string)) {
                    continue;
                }
                log.info("Permit All interface =>" + string);
                stringList.add(string);
            }
            permitMatches = stringList.toArray(new String[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }

//        System.out.println("permitMatches:"+String.join(",",permitMatches));
        // restful具有先天的防范csrf攻击，所以关闭这功能
        http.csrf().disable();
        // 默认允许所有的请求通过，后序我们通过方法注解的方式来粒度化控制权限
        http.authorizeRequests()
                // swagger start
                .antMatchers(comResources).permitAll()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                // swagger end
                .antMatchers(permitMatches).permitAll()
//                .antMatchers("/**").access("hasAuthority('admin')")
//                .antMatchers("admin").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                // 添加属于我们自己的过滤器，注意因为我们没有开启formLogin()，所以UsernamePasswordAuthenticationFilter根本不会被调用
                .addFilterAt(new JWTAuthenticationFilter(jwtAuthenticationManager, redisUtil), UsernamePasswordAuthenticationFilter.class)
                // 前后端分离本身就是无状态的，所以我们不需要cookie和session这类东西。所有的信息都保存在一个token之中。
//                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//                .and().headers().frameOptions().disable()
                .headers().frameOptions().disable()
                .and().logout().disable();
        //禁用缓存
        http.headers().cacheControl();
        //处理Filter
//        http.exceptionHandling().authenticationEntryPoint(entryPointUnauthorizedHandler).accessDeniedHandler(restAccessDeniedHandler);
    }
}
